import { PropType, computed, defineComponent } from "vue";
import Input from "../Input";
import formFieldRef from "../../use/formFieldRef";
import { FeatherChevronDown, FeatherChevronUp } from "cui-vue-icons/feather";

export interface SpinnerProps {
    size?: 'small'|'default'|'large',
    name?: string,
    modelValue?: number,
    max?: number,
    min?: number,
    step?: number,
    loop?: boolean,
    disabled?: boolean
    placeholder?: string
    asFormField?: boolean
}

export default defineComponent({
    name: 'Spinner',
    props: {
        size: {type: String as PropType<SpinnerProps['size']>},
        name: {type: String as PropType<SpinnerProps['name']>},
        modelValue: {type: Number as PropType<SpinnerProps['modelValue']>},
        max: {type: Number as PropType<SpinnerProps['max']>},
        min: {type: Number as PropType<SpinnerProps['min']>},
        step: {type: Number as PropType<SpinnerProps['step']>},
        loop: {type: Boolean as PropType<SpinnerProps['loop']>},
        placeholder: {type: String as PropType<SpinnerProps['placeholder']>},
        disabled: {type: Boolean as PropType<SpinnerProps['disabled']>},
        asFormField: {type: Boolean as PropType<SpinnerProps['asFormField']>, default: true},
    },
    emits: ['change', 'plus', 'sub', 'update:modelValue'],
    setup (props, { emit }) {
        const classList = computed(() => ({
            'cm-spinner': true,
            [`cm-spinner-${props.size}`]: props.size,
            'cm-spinner-disabled': props.disabled
        }));

        const value = formFieldRef(props, emit, Math.max(0, props.min ?? 0));

        const _onInput = (val: string, e: any) => {
            val = val.replace(/[^0-9\.]/g, '');
            e.target.value = val;
        };

        const _onKeyDown = (e: any) => {
            if (e.keyCode === 38) {
                plus();
            }
            if (e.keyCode === 40) {
                sub();
            }
        };

        const min = props.min || 0;
        const step = props.step || 1;
        const _onChange = (v: number) => {
            let val = v;
            if (props.max !== undefined) {
                val = Math.min(val, props.max);
            }
            if (min !== undefined) {
                val = Math.max(val, min);
            }

            Promise.resolve().then(() => {
                value.value = Number(val);
            });
            emit('change', val);
        };

        /**
         * 增加
         * @memberof Spinner
         */
        const plus = () => {
            if (props.disabled) {
                return;
            }
            let v = add(value.value, step);
            if (props.loop && props.max !== undefined && min !== undefined && v > props.max) {
                const off = v - props.max;
                v = min + off - 1;
            }

            if (props.max !== undefined) {
                v = Math.min(props.max, v);
            }

            value.value = v;
            emit('change', v);
            emit('plus', v, step);
        };

        /**
         * 减少
         * @memberof Spinner
         */
        const sub = () => {
            if (props.disabled) {
                return;
            }
            let v = add(value.value, -step);
            if (props.loop && props.max !== undefined && min !== undefined && v < min) {
                const off = v - min;
                v = props.max + off + 1;
            }
            if (min !== undefined) {
                v = Math.max(min, v);
            }

            value.value = v;
            emit('change', v);
            emit('sub', v, step);
        };

        /**
         * 解决数字浮点型双精度问题
         * @param {[type]} num1 [description]
         * @param {[type]} num2 [description]
         */
        function add (num1: number, num2: number): number {
            let r1; let r2;
            try {
                r1 = num1.toString().split('.')[1].length;
            } catch (e) {
                r1 = 0;
            }
            try {
                r2 = num2.toString().split('.')[1].length;
            } catch (e) {
                r2 = 0;
            }
            const m = Math.pow(10, Math.max(r1, r2));
            return (num1 * m + num2 * m) / m;
        }

        return () => <div class={classList.value}>
            <Input size={props.size} placeholder={props.placeholder} disabled={props.disabled} onInput={_onInput} asFormField={false} modelValue={value.value}
                onChange={_onChange} onKeyDown={_onKeyDown} append={
                    <>
                        <span class="cm-spinner-plus" onClick={plus}>
                            <FeatherChevronUp/>
                        </span>
                        <span class="cm-spinner-subs" onClick={sub}>
                            <FeatherChevronDown/>
                        </span>
                    </>
                }/>
        </div>;
    }
});
